package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestBeginIntegration(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "single expression",
			input:    "(begin 1)",
			expected: "1",
		},
		{
			name:     "multiple expressions",
			input:    "(begin 1 2 3)",
			expected: "3",
		},
		{
			name:     "with definitions",
			input:    "(begin (define x 10) x)",
			expected: "10",
		},
		{
			name:     "multiple definitions",
			input:    "(begin (define x 10) (define y 20) (+ x y))",
			expected: "30",
		},
		{
			name:     "with side effects",
			input:    "(begin (define x 1) (set! x 2) x)",
			expected: "2",
		},
		{
			name:     "nested begin",
			input:    "(begin (begin 1 2) 3)",
			expected: "3",
		},
		{
			name:     "empty begin",
			input:    "(begin)",
			expected: "",  // Empty begin returns null object which has empty string representation
		},
		{
			name:     "begin with procedure",
			input:    "(begin (define (square x) (* x x)) (square 5))",
			expected: "25",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Lex
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()

			// Parse
			parser := parse.NewParser(tokens)
			program, err := parser.ParseProgram()
			if err != nil {
				t.Fatalf("Parse error: %v", err)
			}

			// Eval
			var result obj.Object
			for _, expr := range program {
				result, err = EvalExpression(expr, env)
				if err != nil {
					t.Fatalf("Eval error: %v", err)
				}
			}

			// Check result
			if result.Inspect() != tt.expected {
				t.Errorf("Expected %s, got %s", tt.expected, result.Inspect())
			}
		})
	}
}

func TestBeginErrorHandling(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	tests := []struct {
		name        string
		input       string
		expectErr   bool
		description string
	}{
		{
			name:        "begin with invalid expression",
			input:       "(begin (+ 1 2)",
			expectErr:   true,
			description: "Missing closing parenthesis",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Lex
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()

			// Parse
			parser := parse.NewParser(tokens)
			program, err := parser.ParseProgram()
			if err != nil {
				if !tt.expectErr {
					t.Fatalf("Unexpected parse error: %v", err)
				}
				return
			}

			// Eval
			for _, expr := range program {
				_, err = EvalExpression(expr, env)
				if err != nil {
					if !tt.expectErr {
						t.Fatalf("Unexpected eval error: %v (%s)", err, tt.description)
					}
					return
				}
			}

			if tt.expectErr {
				t.Errorf("Expected error, but no error occurred (%s)", tt.description)
			}
		})
	}
}